/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
 
package com.cburch.draw.canvas;

import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class Selection {
    private ArrayList listeners;
    private HashSet selected;
    private Set selectedView;
    private HashSet suppressed;
    private Set suppressedView;
    private CanvasObject handleShape;
    private int handleIndex;
    private int handleDx;
    private int handleDy;
    private int moveDx;
    private int moveDy;
    
    /**
	 * Constructor. Creates several internal storage objects and links them
	 * to Collection objects.
	 * 
	 * 		
	 */
    Selection() {
        listeners = new ArrayList();
        selected = new HashSet();
        suppressed = new HashSet();
        selectedView = Collections.unmodifiableSet(selected);
        suppressedView = Collections.unmodifiableSet(suppressed);
    }
    
    /**
	 * Add a SelectionListener to this object.
	 * 
	 * @param	SelectionListener		listener object to add
	 * @return	void		
	 */
    public void addSelectionListener(SelectionListener l) {
        listeners.add(l);
    }
    
    /**
	 * Remove a SelectionListener from this object.
	 * 
	 * @param	SelectionListener		listener object to remove
	 * @return	void		
	 */
    public void removeSelectionListener(SelectionListener l) {
        listeners.remove(l);
    }
    
    /**
	 * Fire a SelectionEvent. Loops through all listeners attached to
	 * this object, creates a Selection event based on the action and
	 * objects affected and then calls the listeners changed method.
	 * 
	 * @return	Set		this object's suppressedView collection	
	 */
    private void fireChanged(int action, Collection affected) {
        SelectionEvent e = null;
        for(int i = 0, n = listeners.size(); i < n; i++) {
            SelectionListener l = (SelectionListener) listeners.get(i);
            if(e == null) e = new SelectionEvent(this, action, affected);
            l.selectionChanged(e);
        }
    }
    
    /**
	 * Check if the selected objects list is empty.	
	 * 
	 * @return	boolean		true if list is empty	
	 */
    public boolean isEmpty() {
        return selected.isEmpty();
    }
    
    /**
	 * Check if an CanvasObject is contained in the selected objects list.	
	 * 
	 * @return	boolean		true if object is currently selected	
	 */
    public boolean isSelected(CanvasObject shape) {
        return selected.contains(shape);
    }
    
    /**
	 * Get this object's Set of selected CanvasObjects.	
	 * 
	 * @return	Set		the selectedView collection
	 */
    public Set getSelected() {
        return selectedView;
    }
    
    /**
	 * Clear this object's Set of selected CanvasObjects. Saves the previous
	 * selected list, clears the list, then fires a SelectionEvent.	
	 * 
	 * @return	void	
	 */
    public void clearSelected() {
        if(!selected.isEmpty()) {
            ArrayList oldSelected = new ArrayList(selected);
            selected.clear();
            handleShape = null;
            suppressed.clear();
            fireChanged(SelectionEvent.ACTION_REMOVED, oldSelected);
        }
    }
    
    /**
	 * Add or remove a CanvasObject from this object's selected list. Fires a 
	 * selection event upon completion.
	 * 
	 * @param	CanvasObject		shape to add or remove 
	 * @param	boolean		if true add object to selected list, otherwise remove
	 * @return	void	
	 */	
    public void setSelected(CanvasObject shape, boolean value) {
        if(value) {
            if(selected.add(shape)) {
                fireChanged(SelectionEvent.ACTION_ADDED,
                        Collections.singleton(shape));
            }
        } else {
            if(selected.remove(shape)) {
                suppressed.remove(shape);
                if(handleShape == shape) handleShape = null;
                fireChanged(SelectionEvent.ACTION_REMOVED,
                        Collections.singleton(shape));
            }
        }
    }
    
    /**
	 * Get this object's suppressedView Set.
	 * 
	 * @return	Set		this object's suppressedView collection	
	 */
    public Set getDrawsSuppressed() {
        return suppressedView;
    }
    
    /**
	 * Clear this object's suppressedView Set.
	 * 
	 * @return	void
	 */
    public void clearDrawsSuppressed() {
        suppressed.clear();
        handleDx = 0;
        handleDy = 0;
    }
    
    /**
	 * Get this object's handleShape (CanvasObject).
	 * 
	 * @return	CanvasObject		this object's handleShape
	 */
    public CanvasObject getHandleShape() {
        return handleShape;
    }
    
    /**
	 * Get the index of this object's handleShape (CanvasObject).
	 * 
	 * @return	Set		this object's suppressedView collection	
	 */
    public int getHandleIndex() {
        return handleIndex;
    }
    
    /**
	 * Set this object's handleShape.
	 * 
	 * @param	CanvasObject		object to be set as this object's handleShape 
	 * @param	int		index of the CanvasObject that will be the new handleShape
	 * @return	void
	 */
    public void setHandleSelected(CanvasObject shape, int index) {
        handleShape = shape;
        handleIndex = index;
        handleDx = 0;
        handleDy = 0;
    }

    public void setHandleDelta(int dx, int dy) {
        suppressed.add(handleShape);
        handleDx = dx;
        handleDy = dy;
    }
    
    public void setMovingShapes(Collection shapes, int dx, int dy) {
        suppressed.addAll(shapes);
        moveDx = dx;
        moveDy = dy;
    }
    
    public void setMovingDelta(int dx, int dy) {
        moveDx = dx;
        moveDy = dy;
    }
    
    public void drawSuppressed(Graphics g, CanvasObject shape, int xoffs, int yoffs) {
        if(shape == handleShape) {
            shape.draw(g, xoffs, yoffs, handleIndex, handleDx, handleDy);
        } else {
            shape.draw(g, xoffs + moveDx, yoffs + moveDy);
        }
    }

    void modelChanged(CanvasModelEvent event) {
        int action = event.getAction();
        switch(action) {
        case CanvasModelEvent.ACTION_REMOVED:
            Collection affected = event.getAffected();
            if(affected != null) {
                selected.removeAll(affected);
                suppressed.removeAll(affected);
                if(affected.contains(handleShape)) handleShape = null;
            }
            break;
        case CanvasModelEvent.ACTION_HANDLE_DELETED:
        case CanvasModelEvent.ACTION_HANDLE_INSERTED:
            CanvasObject hanShape = handleShape;
            if(hanShape != null && event.getAffected().contains(hanShape)) {
                handleShape = null;
            }
            break;
        }
    }
}
